
/**
	This file is part of MemoryTrace.

	MemoryTrace is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

        Written by Ameziane NAIT ABDELAZIZ.
        
	MemoryTrace is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with MemoryTrace.  If not, see <http://www.gnu.org/licenses/>.
**/

/* 
 * File:   CompressorProcess.cpp
 * Author: maxds
 * 
 * Created on 31 juillet 2011, 15:28
 */

#include <algorithm>
#include "CompressorProcess.h"
#include "ipc.h"
#include "Output.h"



/**
 * Attention le maximum de la trace en mémoire est de 38,1 MB sur 
 * 64 bits GNU/LINUX
 * L'occupation mémoire des backtraces est de 984MB
 */

#define MALLOCS_THRESHOLD 5000000  //10000000=> 381 MB de trace en memoire, 10 millions d'allocations
#define STACK_TRACE_THRESHOLD MALLOCS_THRESHOLD //1032 octets l'entrée en 64 bits
#define BFZ 4096

/**
 * 
 * @param i
 * @return 
 */
alloc_event_t* CompressorProcess::operator [](size_t i){
  return (alloc_event_t*)(shmem_events+i*EVENT_SIZE);
}

CompressorProcess::CompressorProcess() {
}

CompressorProcess::CompressorProcess(const CompressorProcess& orig) {
}

CompressorProcess::~CompressorProcess() {
}


void CompressorProcess::initIPC(){
  
  /*Initialisation d'un segment de memoire partagée pour les evenements d'allocation*/   
  SharedMemory<EVENT_SIZE*MAX_ELEMENTS,IPC_KEY_1>::makeKey();
  SharedMemory<EVENT_SIZE*MAX_ELEMENTS,IPC_KEY_1>::connectToSegment();
  this->shmem_events= SharedMemory<EVENT_SIZE*MAX_ELEMENTS,IPC_KEY_1>::getPointerToShmem();
  
  /*Initialisation d'un segment memoire qui contient le nombre d'evenement actuellement en shmem*/
  SharedMemory<sizeof(unsigned long ),IPC_KEY_2>::makeKey();
  SharedMemory<sizeof(unsigned long ),IPC_KEY_2>::connectToSegment();
  this->shmem_events_count=(unsigned long *)SharedMemory<sizeof(unsigned long ),IPC_KEY_2>::getPointerToShmem();
  
  
  /*initialisation d'une shared memory pour les backtraces*/
#ifndef WITH_PTHREADS
  SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::makeKey();
  SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::connectToSegment();
  this->shmem_stacktraces=SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::getPointerToShmem();
#endif
  
 /*Allocation de la mémoire pour contenir la trace*/
  this->trace_inmem =      (char*)malloc(EVENT_SIZE*MALLOCS_THRESHOLD);
  
  
  /*Initialisation de la mémoire à zero pour que valgrind soit content*/
  memset(this->trace_inmem      ,0,EVENT_SIZE*MALLOCS_THRESHOLD      );
  
  
  /*Asserter si la mémoire merde*/
  assert(this->trace_inmem !=NULL); 
  assert(this->shmem_events_count!=NULL);
  
  
  /*Initialisation des compteurs de trace*/
  this->trace_size  = 0;
  
#ifndef WITH_PTHREADS
  this->stacktrace_size =0;
#endif
  
  /*Allocation de la memoire pour contenir les stacktraces*/
  printf("Initialisation des files de messages\n");
  MESSAGE_QUEUE<QID_1>::initQueues();
  
}



void CompressorProcess::finitIPC(){
  
  //fermeture de la shmem du segment contenant les allocations
  SharedMemory<EVENT_SIZE*MAX_ELEMENTS,IPC_KEY_1>::detachSegment();
  //@warning ! Il faut voir qui va deteruir le segment de mémoire partagée
  SharedMemory<EVENT_SIZE*MAX_ELEMENTS,IPC_KEY_1>::destroyShmem();
  
  //fermeture du segment de memoire contenant les stack traces (origines des allocations)
  SharedMemory<sizeof(unsigned long ),IPC_KEY_2>::detachSegment();
  SharedMemory<sizeof(unsigned long ),IPC_KEY_2>::destroyShmem();
  
#ifndef WITH_PTHREADS
  /*initialisation d'une shared memory pour les backtraces*/
  SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::detachSegment();
  SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::destroyShmem();
#endif
  
  /*supression des MESSAGE_QUEUES*/
  MESSAGE_QUEUE<QID_1>::destroyQueues();    
}




void CompressorProcess::copyToLocalMemoryStackTraces(long count){
 
    //Ici copier dans la locale memory les backtraces
}


void CompressorProcess::copyToLocalMemoryShmemContent(long count){
    
#ifdef DEBUG
  printf("trace_size=%d, count=%d\n",this->trace_size,count);
#endif
  
#ifndef WITH_PTHREADS
#ifdef DEBUG
  printf("stack_trace_size=%d, count=%d\n",this->stacktrace_size,count);
#endif
  /*on demangle chaque element de la shared memory puis on le stock dans la liste*/
  int i=0;
  for(symbole_t* c=((symbole_t*)(this->shmem_stacktraces));i<count;i++,c++){
    
    //printf("[%s]{%s}\n",c->symb,((symbole_t*)(this->shmem_stacktraces))[0].symb);
    
    //@ Pas indispensable de demangler, on peut faire autrement
    /*on demangle*/
    callers_tree* callers_ =demangle_(c);
    /*on stock*/
    this->stack_traces_demangled.push_back(callers_);
    //printf("<<%s %s>>\n",callers_->p_parent->c_str(),callers_->parent->c_str());
  }
#endif

  memcpy(this->trace_inmem+this->trace_size,this->shmem_events,count*EVENT_SIZE);
  trace_size+=count * EVENT_SIZE ;
  
#ifndef WITH_PTHREADS
  stacktrace_size+=count * STACK_TRACE_SIZE;
#endif
  
#ifdef DEBUG
  printf("Copie dans la mémoire locale de %d evenements, taille de la trace = %d KB = %d MB\n",
	 count,this->trace_size/1024,this->trace_size/(1024*1024));
#endif
}


//Cette methode doit être executée par un thread
void CompressorProcess::startWork(){
  
  printf("Working loop form worker\n");
  bool cnt =true;
  
  while(cnt){
    //printf("nombre d'elements en shmem : %d\n",*this->shmem_events_count);
    long typeMsg=MESSAGE_QUEUE<QID_1>::receiveMessageFromProducer(MSG_TAG_ALL);
    
    switch (typeMsg){
    case MSG_TAG_QUIT:
      /*Le producteur nous dit de quitter*/
#ifdef DEBUG
      printf("Je recois le message quitte d'un thread event count %d\n",MESSAGE_QUEUE<QID_1>::getEventsCount());
#endif
      /*On copie le reste des evenements dans la mémoire locale*/
      this->copyToLocalMemoryShmemContent(MESSAGE_QUEUE<QID_1>::getEventsCount());
      cnt=false;
      break;
      
      /*Le producteur nous dit que la shmem est remplie*/
    case MSG_TAG_PLEIN:
#ifdef DEBUG
      printf("Je recois le message shmem est pleinne\n");
#endif
      /*on copie localement le contenu de la shmem*/
      this->copyToLocalMemoryShmemContent(*this->shmem_events_count);
      
#ifndef WITH_PTHREADS
      /*on copie localement les backtraces*/
      this->copyToLocalMemoryStackTraces(MESSAGE_QUEUE<QID_1>::getEventsCount());       
#endif
      /*on signale au producteur que c'est fait*/
      MESSAGE_QUEUE<QID_1>::sendMessageToProducer(MSG_TAG_RECU);
      break;
      
    default:
      break;
      
    }
  }
}


#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <algorithm>



#define BUFFER_SIZE EVENT_SIZE*200

void CompressorProcess::writeMemoryTraceToFile(){
  /*ouverture du fichier en mode ecriture*/
  int out=open("memoryTrace.bsf",O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
  assert(out != -1);  
  
  Output::showMessageEcritureTrace(this->trace_size);
  
  size_t ret =-1;
  
  ret=write(out, this->trace_inmem, trace_size);
  
  //@ Attention, il faut tester la valeur de retour
  assert(ret!=-1);
  
  close(out);
  Output::showMessageTraceSize(trace_size/EVENT_SIZE);
  
  /*liberation de la mémoire occupée par la trace en mémoire*/ 
  free(this->trace_inmem);
}

void CompressorProcess::writeBacktracesToFile(bool yes_or_no){

   
#ifndef WITH_PTHREADS 
  if(yes_or_no == false)
    return ;
  
  /*on écrit les backtraces dans un fichier de sortie*/
  FILE* btout=fopen("backTraces.bt","w+");
  int i=0;
  callers_tree* current;
  
  Output::showMessageEcritureBackTrace();
  
  for(list<callers_tree*>::iterator it(this->stack_traces_demangled.begin()); it!=this->stack_traces_demangled.end();++it){
    i++;
    current=(callers_tree*)(*it);
    //format =>  id|parent|pparent
    fprintf(btout,"%d|%s|%s\n",i,(current->parent==NULL)?"0"  :current->parent->c_str(),
	    (current->p_parent==NULL)?"0":current->p_parent->c_str());
    
    delete current;
  }
  fclose(btout);
#endif
}
